package unimelb.discussiontracker.twitter.data.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

import unimelb.discussiontracker.twitter.common.QueryControl;
import unimelb.discussiontracker.twitter.data.dao.IDataAccessObject;

@Repository
public abstract class GenericDataAccessObject<T> implements IDataAccessObject<T> {

    @PersistenceContext
    private EntityManager entityManager;

    private Class<T> entityType;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public GenericDataAccessObject() {
        super();
        final ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        entityType = (Class) pt.getActualTypeArguments()[0];
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public T save(T object) {
        entityManager.persist(object);
        return object;
    }

    @Override
    public T findById(long id) {
        return entityManager.find(entityType, id);
    }

    @Override
    public T update(T object) {
        return entityManager.merge(object);
    }

    @Override
    public void delete(long id) {
        final T entity = this.findById(id);
        if (entity != null) {
            entityManager.remove(entity);
        }
    }

    @SuppressWarnings("unchecked")
    protected List<T> runNamedQuery(String queryName, Map<String, Object> params, QueryControl queryControl) {
        final Query query = entityManager.createNamedQuery(queryName);
        return prepareQuery(query, params, queryControl).getResultList();
    }

    protected T runSingleResultNamedQuery(String queryName, Map<String, Object> params, QueryControl queryControl) {
        final List<T> results = runNamedQuery(queryName, params, queryControl);
        return results.size() > 0 ? results.get(0) : null;
    }

    @SuppressWarnings("rawtypes")
    protected List runDynamicQuery(String queryString, Map<String, Object> params, QueryControl queryControl) {
        final Query query = entityManager.createQuery(queryString);
        return prepareQuery(query, params, queryControl).getResultList();
    }

    protected long runCountQuery(String queryName, Map<String, Object> params, QueryControl queryControl) {
        final Query query = entityManager.createNamedQuery(queryName);
        return (Long) prepareQuery(query, params, queryControl).getSingleResult();
    }

    private Query prepareQuery(Query query, Map<String, Object> params, QueryControl queryControl) {
        if (queryControl != null) {
            query.setMaxResults(queryControl.getPageSize());
            query.setFirstResult((queryControl.getPageNumber() - 1) * queryControl.getPageSize());
        }

        if (params != null) {
            for (Entry<String, Object> param : params.entrySet()) {
                query.setParameter(param.getKey(), param.getValue());
            }
        }

        return query;
    }
}
